Popular Searches
Popular Course Categories
Popular Courses

Applying Bean Validation to Request Data

Applying Bean Validation to Request Data

Spring AOP, Validation, and Exception Management

Applying Bean Validation to Request Data in Java

Course: Advanced Java Training

Topic: Applying Bean Validation to Request Data

Course Link: Advanced Java Training – JustAcademy

Demo Link: Register for a Free Course Demo

Official Website: JustAcademy


1. Introduction to Bean Validation

Bean Validation is a Java validation mechanism used to check whether application data satisfies predefined rules. It helps ensure that incoming request data meets the requirements of an application before the data is processed, saved, or used in business operations.

In Spring Boot applications, Bean Validation is commonly used to validate REST API request bodies, query parameters, path variables, form inputs, and DTO (Data Transfer Object) objects. For example, a registration API may require a valid email address, a non-empty name, and a password of a minimum length.

Instead of writing repetitive validation logic inside every controller method, developers can declare validation rules directly on DTO fields using annotations. Spring can then trigger these rules automatically when request data is received.

Why Is Bean Validation Important?

  • Prevents invalid data from entering business logic.
  • Reduces repetitive manual validation code.
  • Provides clear and reusable validation rules.
  • Improves REST API reliability and consistency.
  • Helps return meaningful error messages to API clients.
  • Supports validation at both individual-field and object levels.
  • Improves maintainability by separating validation rules from controller logic.

2. Understanding Jakarta Bean Validation

Jakarta Bean Validation defines a standard API for declaring and executing validation constraints on Java objects. Modern Spring Boot applications commonly use Jakarta Validation packages, such as jakarta.validation.Valid and jakarta.validation.constraints.NotBlank.

Older Java and Spring applications may use the former javax.validation package. The correct package depends on the framework version. Spring Boot 3 and later use Jakarta EE namespaces.

Main Components

Component Description
Constraint Annotation Declares a validation rule, such as @NotNull or @Email.
Validator Provider Executes validation rules. Hibernate Validator is a commonly used implementation.
@Valid Requests validation of an object and supports cascading into nested objects.
@Validated Spring annotation that supports validation integration and, in supported contexts, validation groups.
ConstraintViolation Represents a validation failure, including the property path and message.
BindingResult In Spring MVC, can hold validation errors when placed immediately after the validated method argument.

3. Setting Up Bean Validation in Spring Boot

For a typical Spring Boot web application, add the validation starter to enable Bean Validation integration. Maven projects can use the following dependency:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-validation</artifactId>
</dependency>

Use the version managed by your Spring Boot parent or dependency-management configuration. In a standard Spring Boot web application, this starter provides the validation API, a provider, and framework integration.

Gradle Dependency

dependencies {
    implementation("org.springframework.boot:spring-boot-starter-validation")
}

After adding the dependency, reload the Maven or Gradle project. If your application already includes the validation starter through another dependency, avoid adding a duplicate declaration unnecessarily.

4. Common Bean Validation Annotations

Bean Validation provides several built-in constraints for checking common data requirements.

Annotation Purpose Example
@NotNull Rejects a null value. @NotNull private Long id;
@NotEmpty Rejects null or empty character sequences, collections, maps, or arrays. @NotEmpty private List<String> tags;
@NotBlank Rejects null, empty, or whitespace-only character sequences. @NotBlank private String name;
@Size Checks supported value sizes or character-sequence length. @Size(min = 2, max = 60)
@Min Requires a numeric value to be at least the specified minimum. @Min(18)
@Max Requires a numeric value not to exceed the specified maximum. @Max(100)
@Positive Requires a positive numeric value. @Positive
@Email Checks whether a character sequence resembles a valid email address. @Email
@Pattern Checks a character sequence against a regular expression. @Pattern(regexp = "^[A-Z]{2}$")
@Past Requires a supported date or time value to be in the past. @Past
@Future Requires a supported date or time value to be in the future. @Future
@DecimalMin Checks a decimal value against a minimum. @DecimalMin("0.01")
@DecimalMax Checks a decimal value against a maximum. @DecimalMax("99999.99")

Important Differences

  • @NotNull allows an empty string but rejects null.
  • @NotEmpty rejects null and empty values but allows a string containing only spaces.
  • @NotBlank is useful for text fields that must contain non-whitespace characters.
  • @Size does not automatically make a field mandatory. Combine it with an appropriate presence constraint when needed.
  • @Email provides format-oriented validation, not proof that an email address exists or belongs to the user.
  • Numeric constraints generally consider a null value valid, so combine them with @NotNull when the field is required.

5. Creating a Request DTO with Validation Rules

A DTO defines the fields that an API client can submit. Validation annotations can be placed directly on these fields to describe the acceptable input.

Example: User Registration DTO

package com.example.demo.dto;

import jakarta.validation.constraints.Email;
import jakarta.validation.constraints.Min;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotNull;
import jakarta.validation.constraints.Size;

public class UserRegistrationRequest {

    @NotBlank(message = "Name is required")
    @Size(min = 2, max = 50,
          message = "Name must contain between 2 and 50 characters")
    private String name;

    @NotBlank(message = "Email is required")
    @Email(message = "Enter a valid email address")
    private String email;

    @NotNull(message = "Age is required")
    @Min(value = 18, message = "Age must be at least 18")
    private Integer age;

    @NotBlank(message = "Password is required")
    @Size(min = 8, max = 100,
          message = "Password must contain between 8 and 100 characters")
    private String password;

    public UserRegistrationRequest() {
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getEmail() {
        return email;
    }

    public void setEmail(String email) {
        this.email = email;
    }

    public Integer getAge() {
        return age;
    }

    public void setAge(Integer age) {
        this.age = age;
    }

    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }
}

In this example, each field has rules that describe acceptable input. If the request contains a blank name, malformed email address, missing age, age below 18, or password outside the specified length, validation can reject the request before the controller proceeds with normal processing.

Security note: The password field should never be returned in API responses or written to application logs. A production application should hash passwords using an appropriate password-hashing algorithm before storing them.

6. Applying Validation in a REST Controller

To validate a JSON request body, annotate the controller method parameter with @Valid. Spring MVC then validates the deserialized DTO before invoking the controller method body normally.

package com.example.demo.controller;

import com.example.demo.dto.UserRegistrationRequest;
import jakarta.validation.Valid;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;

import java.util.Map;

@RestController
@RequestMapping("/api/users")
public class UserController {

    @PostMapping
    public ResponseEntity<Map<String, String>> registerUser(
            @Valid @RequestBody UserRegistrationRequest request) {

        // Call the service layer after validation succeeds.
        // Persist the user only after applying business rules
        // and securely hashing the password.

        return ResponseEntity.status(HttpStatus.CREATED)
                .body(Map.of(
                        "message", "User registration request is valid"
                ));
    }
}

How the Validation Process Works

  1. The client sends an HTTP request containing JSON data.
  2. Spring converts the JSON payload into a Java DTO.
  3. The @Valid annotation triggers Bean Validation.
  4. The validation provider evaluates the DTO constraints.
  5. If validation succeeds, the controller method executes.
  6. If validation fails, Spring MVC raises a validation exception, which can be handled to return an appropriate client error.

For a request-body validation failure in Spring MVC, the common exception is MethodArgumentNotValidException. Applications should handle it centrally when they need a consistent error-response format.

7. Handling Validation Errors with Global Exception Handling

Returning clear validation messages helps API clients correct their requests. A global exception handler can collect field errors and return them in a predictable JSON structure.

package com.example.demo.exception;

import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;

import java.util.LinkedHashMap;
import java.util.Map;

@RestControllerAdvice
public class GlobalValidationExceptionHandler {

    @ExceptionHandler(MethodArgumentNotValidException.class)
    public ResponseEntity<Map<String, Object>> handleValidationErrors(
            MethodArgumentNotValidException exception) {

        Map<String, String> fieldErrors = new LinkedHashMap<>();

        exception.getBindingResult().getFieldErrors().forEach(error ->
            fieldErrors.putIfAbsent(
                    error.getField(),
                    error.getDefaultMessage()
            )
        );

        Map<String, Object> response = new LinkedHashMap<>();
        response.put("status", HttpStatus.BAD_REQUEST.value());
        response.put("message", "Request validation failed");
        response.put("errors", fieldErrors);

        return ResponseEntity.badRequest().body(response);
    }
}

Example Error Response

{
  "status": 400,
  "message": "Request validation failed",
  "errors": {
    "name": "Name is required",
    "email": "Enter a valid email address",
    "age": "Age must be at least 18"
  }
}

The response uses HTTP 400 Bad Request because the client submitted data that does not satisfy the API's validation rules. The exact response structure is an application design choice; teams should document and use a consistent format.

The example keeps the first error for each field. If an API needs to return every failed constraint for a field, collect errors into a list rather than using one message per field.

8. Validating Query Parameters and Path Variables

Request data is not limited to JSON request bodies. Query parameters and path variables may also require validation. In Spring MVC, direct method-parameter constraints such as @Min, @Max, and @NotBlank require method validation support. The precise exception and setup depend on the Spring version and controller configuration.

Example: Validating a Query Parameter

import jakarta.validation.constraints.Max;
import jakarta.validation.constraints.Min;
import org.springframework.web.bind.annotation.*;

@RestController
@RequestMapping("/api/products")
public class ProductController {

    @GetMapping
    public String getProducts(
            @RequestParam(defaultValue = "1")
            @Min(value = 1, message = "Page must be at least 1")
            @Max(value = 1000, message = "Page must not exceed 1000")
            int page) {

        return "Requested page: " + page;
    }
}

Depending on the Spring Framework version and method-validation configuration, direct parameter constraints may be validated through Spring's method-validation support. In Spring Framework 6.1 and later, MVC has built-in controller method validation behavior. Older versions may require class-level @Validated and the appropriate method-validation infrastructure. Avoid adding @Validated indiscriminately to newer MVC controllers without understanding the exception handling behavior.

9. Nested Object Validation

Some request DTOs contain other objects. For example, a customer request may include an address object. Use @Valid on the nested field so its constraints are checked when the parent DTO is validated.

Address DTO

import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.Size;

public class AddressRequest {

    @NotBlank(message = "Street is required")
    private String street;

    @NotBlank(message = "City is required")
    private String city;

    @NotBlank(message = "Postal code is required")
    @Size(max = 12, message = "Postal code is too long")
    private String postalCode;

    public String getStreet() {
        return street;
    }

    public void setStreet(String street) {
        this.street = street;
    }

    public String getCity() {
        return city;
    }

    public void setCity(String city) {
        this.city = city;
    }

    public String getPostalCode() {
        return postalCode;
    }

    public void setPostalCode(String postalCode) {
        this.postalCode = postalCode;
    }
}

Parent DTO with Cascaded Validation

import jakarta.validation.Valid;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotNull;

public class CustomerRequest {

    @NotBlank(message = "Customer name is required")
    private String name;

    @NotNull(message = "Address is required")
    @Valid
    private AddressRequest address;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public AddressRequest getAddress() {
        return address;
    }

    public void setAddress(AddressRequest address) {
        this.address = address;
    }
}

The @NotNull annotation ensures that the address object is present, while @Valid validates the fields inside it. These annotations serve different purposes and are often used together.

10. Validating Collections and List Items

Request payloads may contain arrays or lists of objects. Container-element constraints can validate individual elements, while @Valid can cascade validation into each object.

import jakarta.validation.Valid;
import jakarta.validation.constraints.NotEmpty;
import jakarta.validation.constraints.NotNull;
import java.util.List;

public class OrderRequest {

    @NotEmpty(message = "An order must contain at least one item")
    private List<@NotNull @Valid OrderItemRequest> items;

    public List<OrderItemRequest> getItems() {
        return items;
    }

    public void setItems(List<OrderItemRequest> items) {
        this.items = items;
    }
}

Example item DTO:

import jakarta.validation.constraints.Min;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotNull;

public class OrderItemRequest {

    @NotBlank(message = "Product code is required")
    private String productCode;

    @NotNull(message = "Quantity is required")
    @Min(value = 1, message = "Quantity must be at least 1")
    private Integer quantity;

    public String getProductCode() {
        return productCode;
    }

    public void setProductCode(String productCode) {
        this.productCode = productCode;
    }

    public Integer getQuantity() {
        return quantity;
    }

    public void setQuantity(Integer quantity) {
        this.quantity = quantity;
    }
}

Collection validation can confirm that a list is present and non-empty, that elements are not null, and that each item meets its field constraints. Business rules such as stock availability and maximum order quantity still belong in the appropriate service or domain layer.

11. Validating Request Data with Custom Constraints

Built-in annotations cover many common requirements. When a rule cannot be expressed cleanly using standard constraints, you can create a custom constraint annotation and implement its validator.

For example, suppose a request must contain a company code that matches a specific format.

Step 1: Create a Custom Annotation

import jakarta.validation.Constraint;
import jakarta.validation.Payload;
import java.lang.annotation.Documented;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;

import static java.lang.annotation.ElementType.FIELD;
import static java.lang.annotation.ElementType.PARAMETER;
import static java.lang.annotation.RetentionPolicy.RUNTIME;

@Documented
@Constraint(validatedBy = CompanyCodeValidator.class)
@Target({FIELD, PARAMETER})
@Retention(RUNTIME)
public @interface ValidCompanyCode {

    String message() default "Company code must contain 3 uppercase letters followed by 3 digits";

    Class<?>[] groups() default {};

    Class<? extends Payload>[] payload() default {};
}

Step 2: Implement the Validator

import jakarta.validation.ConstraintValidator;
import jakarta.validation.ConstraintValidatorContext;

public class CompanyCodeValidator
        implements ConstraintValidator<ValidCompanyCode, String> {

    @Override
    public boolean isValid(
            String value,
            ConstraintValidatorContext context) {

        // Null handling is normally delegated to @NotNull
        // when the field is mandatory.
        if (value == null) {
            return true;
        }

        return value.matches("[A-Z]{3}[0-9]{3}");
    }
}

Step 3: Apply the Custom Constraint

import jakarta.validation.constraints.NotBlank;

public class CompanyRequest {

    @NotBlank(message = "Company code is required")
    @ValidCompanyCode
    private String companyCode;

    public String getCompanyCode() {
        return companyCode;
    }

    public void setCompanyCode(String companyCode) {
        this.companyCode = companyCode;
    }
}

Valid examples include ABC123 and XYZ789. Values such as ab1234 or ABC12 fail the custom format rule.

A custom validator should generally be deterministic and fast. Avoid database queries or remote service calls inside a constraint validator unless the design specifically accounts for performance, transaction boundaries, and error handling. Rules requiring database uniqueness or changing business state are often better checked in the service layer.

12. Cross-Field Validation

Some rules depend on multiple fields rather than a single property. For example, an event's end date must be after its start date, or a confirmation password must match the original password.

Cross-field validation can be implemented with a class-level constraint. The validator receives the complete DTO and compares related values. A typical approach is to create an annotation targeting ElementType.TYPE, implement a validator for the DTO class, and return whether the combined rule is satisfied.

For a date range, the core comparison may look like this:

if (request.getStartDate() == null
        || request.getEndDate() == null) {
    return true; // Presence constraints handle missing dates.
}

return request.getEndDate().isAfter(request.getStartDate());

In a complete implementation, attach the validation error to a useful property path, such as endDate, by using ConstraintValidatorContext. This helps API clients identify the field they should correct.

13. Validation Groups

Validation groups let an application apply different constraints in different situations. For example, creating a record may require all fields, while updating a record may allow some fields to remain unchanged.

Define marker interfaces to represent validation groups:

public interface CreateOperation {
}

public interface UpdateOperation {
}

Assign constraints to groups:

import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotNull;

public class ProductRequest {

    @NotBlank(
        message = "Name is required",
        groups = CreateOperation.class
    )
    private String name;

    @NotNull(
        message = "Product ID is required for update",
        groups = UpdateOperation.class
    )
    private Long id;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }
}

Groups can be selected through Spring's validation integration, including @Validated(CreateOperation.class) where appropriate. Group behavior should be tested carefully, particularly when default-group constraints, nested validation, and method validation are combined.

14. Bean Validation vs. Business Validation

Bean Validation and business validation are related, but they solve different problems.

Bean Validation Business Validation
Checks structural and field-level constraints. Checks domain rules and operational conditions.
Examples: required name, email format, quantity greater than zero. Examples: account has sufficient balance, product is in stock, customer is eligible for a discount.
Usually declared through annotations and validators. Usually implemented in service or domain logic.
Often runs before the controller processes the request. Runs when application context or business state must be evaluated.
Should provide clear input-related errors. Should provide meaningful domain errors and preserve business invariants.

For example, @Min(1) can ensure that an order quantity is positive. It cannot guarantee that the requested quantity is available in inventory. That check must use current application data in the service or domain layer.

15. Testing Bean Validation

Validation should be tested at both the DTO level and the API level. Unit tests help verify individual constraints, while controller integration tests confirm that Spring applies validation and returns the intended HTTP response.

Unit Test Using Validator

import com.example.demo.dto.UserRegistrationRequest;
import jakarta.validation.Validation;
import jakarta.validation.Validator;
import jakarta.validation.ValidatorFactory;
import jakarta.validation.ConstraintViolation;
import org.junit.jupiter.api.Test;

import java.util.Set;

import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertTrue;

class UserRegistrationRequestTest {

    private final Validator validator;

    UserRegistrationRequestTest() {
        ValidatorFactory factory =
                Validation.buildDefaultValidatorFactory();
        this.validator = factory.getValidator();
    }

    @Test
    void shouldRejectInvalidRegistrationData() {
        UserRegistrationRequest request =
                new UserRegistrationRequest();

        request.setName(" ");
        request.setEmail("invalid-email");
        request.setAge(15);
        request.setPassword("123");

        Set<ConstraintViolation<UserRegistrationRequest>> violations =
                validator.validate(request);

        assertFalse(violations.isEmpty());
    }

    @Test
    void shouldAcceptValidRegistrationData() {
        UserRegistrationRequest request =
                new UserRegistrationRequest();

        request.setName("Aarav Sharma");
        request.setEmail("[email protected]");
        request.setAge(25);
        request.setPassword("SecurePass123");

        Set<ConstraintViolation<UserRegistrationRequest>> violations =
                validator.validate(request);

        assertTrue(violations.isEmpty());
    }
}

These tests validate the DTO constraints directly. The validator factory can also be managed through a test lifecycle when many tests share the same setup.

Suggested API Test Cases

  • Submit a valid request and verify the expected success status.
  • Submit an empty name and verify HTTP 400.
  • Submit an invalid email address and check the error response.
  • Omit a required numeric field and verify that the presence constraint fails.
  • Send a numeric value below its allowed minimum.
  • Send an invalid nested address or order item.
  • Verify that invalid requests do not trigger persistence or unintended business operations.
  • Verify that error responses do not expose passwords, stack traces, or internal implementation details.

16. Common Errors and Troubleshooting

Problem Possible Cause Solution
Validation annotations appear to be ignored. The validation starter is missing or @Valid is not applied to the relevant request object. Add the appropriate validation dependency and check the controller parameter.
Wrong validation imports. Code mixes legacy javax.validation imports with Jakarta-based dependencies. Use jakarta.validation for modern Spring Boot 3+ projects.
Missing values pass a minimum or maximum constraint. Numeric constraints generally treat null as valid. Add @NotNull if the value is mandatory.
Whitespace-only text passes. @NotEmpty does not reject whitespace-only strings. Use @NotBlank for required text.
Nested fields are not validated. Cascaded validation is missing on the nested property. Add @Valid to the nested field and define its constraints.
API returns an unexpected error format. Validation exceptions are not handled consistently. Implement centralized exception handling and test the response contract.
Query parameter constraints do not run. Method validation configuration or framework-version behavior differs. Check Spring Framework version and use the supported controller method-validation mechanism.
Validation passes but the operation still fails. Field constraints do not cover business rules or current database state. Implement service-layer checks and appropriate transaction handling.
Custom validator behaves unexpectedly. Null handling, annotation targets, or validator type parameters may be incorrect. Check the constraint definition, validator implementation, and associated tests.

17. Best Practices for Request Validation

  • Use dedicated request DTOs rather than exposing persistence entities directly as API input models.
  • Choose annotations according to the actual business requirement.
  • Use @NotBlank for required text fields and @NotNull for required object or numeric fields.
  • Keep validation messages understandable and suitable for API consumers.
  • Centralize error handling using @RestControllerAdvice where a consistent API contract is needed.
  • Validate nested objects and collection elements when accepting structured request data.
  • Use custom constraints for reusable rules that standard annotations cannot express clearly.
  • Keep database-dependent business checks in the service or domain layer.
  • Never treat input validation as a substitute for authorization, authentication, parameterized database access, or output encoding.
  • Do not expose internal exception details or sensitive request fields in error responses and logs.
  • Write unit and integration tests for valid, invalid, boundary, and missing input values.
  • Document expected validation errors in API documentation.
  • Remember that syntactic validation cannot guarantee that external facts, such as email ownership, are true.

18. Practical Exercise

Task: Build a Spring Boot REST endpoint that accepts a product creation request and validates all incoming fields.

Requirements

  • Create a ProductRequest DTO containing name, description, price, quantity, and category.
  • Require a non-blank product name with a maximum length of 100 characters.
  • Allow an optional description with a maximum length of 500 characters.
  • Require a price greater than zero.
  • Require a quantity of zero or greater.
  • Require a non-blank category.
  • Use @Valid in the controller to validate the request body.
  • Return a structured HTTP 400 response for validation failures.
  • Write tests for missing names, negative prices, negative quantities, and valid product data.
  • Keep stock availability and category existence checks in the service layer if those checks depend on application data.

Example DTO

import jakarta.validation.constraints.DecimalMin;
import jakarta.validation.constraints.Min;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotNull;
import jakarta.validation.constraints.Size;
import java.math.BigDecimal;

public class ProductRequest {

    @NotBlank(message = "Product name is required")
    @Size(max = 100, message = "Product name cannot exceed 100 characters")
    private String name;

    @Size(max = 500, message = "Description cannot exceed 500 characters")
    private String description;

    @NotNull(message = "Price is required")
    @DecimalMin(value = "0.01", message = "Price must be greater than zero")
    private BigDecimal price;

    @NotNull(message = "Quantity is required")
    @Min(value = 0, message = "Quantity cannot be negative")
    private Integer quantity;

    @NotBlank(message = "Category is required")
    private String category;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getDescription() {
        return description;
    }

    public void setDescription(String description) {
        this.description = description;
    }

    public BigDecimal getPrice() {
        return price;
    }

    public void setPrice(BigDecimal price) {
        this.price = price;
    }

    public Integer getQuantity() {
        return quantity;
    }

    public void setQuantity(Integer quantity) {
        this.quantity = quantity;
    }

    public String getCategory() {
        return category;
    }

    public void setCategory(String category) {
        this.category = category;
    }
}

19. Interview Questions and Answers

Q1. What is Bean Validation in Java?

Answer: Bean Validation is a standard mechanism for declaring and executing constraints on Java object properties, method parameters, and return values. It helps ensure that data satisfies defined rules before further processing.

Q2. What is the purpose of @Valid in Spring Boot?

Answer: @Valid triggers validation of a supported object argument and enables cascaded validation of nested objects when those properties are also annotated appropriately.

Q3. What is the difference between @NotNull, @NotEmpty, and @NotBlank?

Answer: @NotNull rejects null values, @NotEmpty rejects null or empty supported values, and @NotBlank rejects null, empty, or whitespace-only character sequences.

Q4. What is Hibernate Validator?

Answer: Hibernate Validator is a commonly used implementation of the Jakarta Bean Validation specification. It evaluates constraints and reports violations when values do not satisfy their rules.

Q5. How do you validate a REST API request body?

Answer: Define constraints on a request DTO, add the validation dependency, and annotate the controller's @RequestBody parameter with @Valid. Handle validation failures through Spring's exception-handling mechanisms.

Q6. What is @RestControllerAdvice used for?

Answer: It provides centralized handling of exceptions raised by REST controllers, including request validation errors, so an application can return consistent response structures.

Q7. What is a custom validation annotation?

Answer: It is an application-defined constraint annotation connected to a validator implementation. It is useful when built-in annotations cannot express a reusable validation rule.

Q8. How do you validate nested request objects?

Answer: Add @Valid to the nested property and place the relevant constraints on fields in the nested DTO. Use @NotNull as well if the nested object itself is mandatory.

Q9. Can Bean Validation replace business validation?

Answer: No. Bean Validation is suitable for constraints such as required fields and value ranges. Business validation handles rules involving domain behavior, database state, inventory, permissions, and other contextual conditions.

Q10. Why can @Min allow a null value?

Answer: Numeric constraints generally do not enforce presence. If a field must be supplied, combine the numeric constraint with @NotNull.

Q11. How can validation errors be returned as JSON?

Answer: Use @RestControllerAdvice with an exception handler for the relevant Spring validation exception. Extract field errors and return them in a structured response with HTTP 400.

Q12. What are validation groups?

Answer: Validation groups allow an application to execute selected constraints for different use cases, such as creating a new record or updating an existing one.

20. Quick Revision Checklist

  • Bean Validation declares reusable constraints on Java data.
  • Spring Boot projects commonly use spring-boot-starter-validation.
  • Modern Spring Boot 3+ projects use the jakarta.validation package.
  • DTO fields can use annotations such as @NotBlank, @Email, @Size, @Min, and @DecimalMin.
  • Use @Valid to validate request objects and cascade into nested DTOs.
  • Use @NotNull alongside numeric constraints when a value is mandatory.
  • Use @RestControllerAdvice to create consistent validation-error responses.
  • Custom constraints support specialized reusable rules.
  • Cross-field rules can use class-level constraints.
  • Validation groups support different sets of constraints for different operations.
  • Bean Validation does not replace authorization or service-layer business rules.
  • Test both the DTO constraints and the HTTP endpoint behavior.

21. Conclusion

Applying Bean Validation to request data is an important part of building reliable Spring Boot REST APIs. By defining constraints on DTOs, using @Valid, validating nested collections, and handling errors centrally, developers can reject invalid input before it reaches application logic.

Effective validation also requires choosing the right annotation, writing tests, protecting sensitive data, and separating simple input constraints from business rules that depend on application state. These practices make APIs easier to maintain, safer to use, and more predictable for clients.

To learn more about Java, Spring Boot, REST APIs, and backend development, explore the Advanced Java Training course at JustAcademy. You can also register for a course demo or visit the JustAcademy website.

Previous Next
whatsapp